package org.NooLab.glue.components;

import java.util.ArrayList;
import java.util.Observer;
import java.util.SortedMap;
import java.util.Vector;



import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.net.connex.FindFreePort;
import org.NooLab.utilities.net.connex.NicAddresses;
import org.NooLab.utilities.strings.ArrUtilities;
 
import org.NooLab.compare.*;
import org.NooLab.compare.utilities.math.DistanceControlProperties;
 
import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.interfaces.*;
import org.NooLab.glue.instances.*;
import org.NooLab.glue.msgframe.*;
import org.NooLab.glue.transaction.*;

import org.NooLab.glue.storage.*;

import org.NooLab.glue.filter.*;
import org.NooLab.glue.subscription.*;
import org.NooLab.glue.subscription.FilterPackage;
import org.NooLab.glue.subscription.FilterPackageIntf;
import org.NooLab.glue.subscription.context.Comparison;
import org.NooLab.glue.subscription.context.ComparisonFactory;

import org.NooLab.glue.net.*;
import org.NooLab.glue.net.env.*;
 

/**
 * 
 * see: http://download.oracle.com/javase/1.4.2/docs/guide/idl/jidlExample3.html
 * for callbacks across public internet: Java IDL
 * 
 * see: http://www.java2s.com/Code/Java/Network-Protocol/HttpconnectionUtilities.htm
 * 
 */
public class MessageBoardFactory implements TransportsInformerIntf{

	// =================================
 
	
	// object references .............. 
	
	MessageBoardFactory mbf ;
	MessageBoardFactoryProperties factoryProperties;
	
	MessageBoardShare msgBoardShare ;
	GlobalReceipts globalReceipts;
	MetaRegistration metaRegistration;
	
	protected MessageBoard mb=null, mb2=null;
	MessageBoardConnector msgBoardConnector;
	
	MsgBoardDataStorage msgBoardStorage; // in case of instance = MessageBoard

	MessageBoardMonitor msgBoardMonitor;
	
	// this get instantiated outside of construction
	protected MsgBoardDataStorage participantsStorage ;
	
 	private ParticipantsDeliveryPad deliveryPad;
 	private MessageReceptor messageReceptor;
 	private MessageBoardParticipant participant ;
 	private TouchBox touchBox ;
   
	Transactions transactions ;
	
	// if the instance is of type MessageBoard, we will provide it over there 
	Futures futures;
	CompareIntf compareInstance=null;
	CompareFactory compareFactory ;
	ComparisonFactory comparisonFactory;
	
	
	ComBoxFactory comBoxFactory , comBoxFactory2;
	
	
	FilterPackage filterPkg = null;
	FilterPackageIntf filterPackage = null;
	
	private BoardConnectorIntf boardConnectorProcedures ;  // the method performed in the engine
	private BoardUpLinkServiceIntf boardConnectService; 
	
	
	BoardAddresses boardAddresses ;
	
	
	// main variables / properties ....

	String configPath ;
	boolean msgfilterIsActivated = false ;
	
	Vector<Integer> reservedPorts = new Vector<Integer> (); 
	
	int protocolID ;
	int secondaryProtocolID ;
	
	boolean directConnectionsAmongParticipants = false; // MessageBoard would be (also) a name-server
	
	Observer receptorObserver ;
			
	// constants ......................
	
	int servicePort = 7111 ;

	ArrayList<String> ipAddressList ; 
	String ipAddressListStr="" ;
	
	// volatile variables .............
	
	String participantsRole="";
	int factoryMode ;
	
	Vector<XRegistration> registeredInstances = new Vector<XRegistration>();
	
	int registrationsActive=0;
	Registration registration;
	
	RegistrationSets regSets = new RegistrationSets();
	
	Vector<Integer> issuedTickets = new Vector<Integer>();
	int latestTicketValue = 1;
	
	int connectError = 0;
	// we need a wrapping object for the collection of openTransactions, adding save/restor 
	OpenTransactions openTransactions;
	
	OpenDataDeliveries openDataDeliveries ;
	
	boolean remoteInstances;
	boolean messageBoardisStopping = false;
	
	
	// helper objects .................

	ArrUtilities arrutil = new ArrUtilities (); 
	FindFreePort freePort = new FindFreePort() ;
	PrintLog out = new PrintLog(2,false);
	
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public MessageBoardFactory( MessageBoardFactoryProperties[] factorypropertieslist ) throws Exception{
		// if we would like to create several MessageBoards
		
		// or should we create different instances of MessageBoardFactory ? 
		// createMessageBaordFromFactoryProperties( factoryPropertieslist[0] );
		
	}
	
	public MessageBoardFactory( MessageBoardFactoryProperties factoryproperties ) throws Exception{
		
		factoryProperties = factoryproperties;

		msgBoardShare = new MessageBoardShare(this) ;
		
		
/*  these objects need to be aligned, all other fields are just "data"
	transient ParticipantsSettings pSettings;
	
	transient Observer observingObj ;
	transient TransportsInformerIntf transportsInformer;
	transient ClientMatcherIntf clientMatcher;
	transient Object parent ;
	transient DFutils fileutil = new DFutils();
	transient StringsUtil strgutil = new StringsUtil(); 
*/
		if (factoryProperties.getProtocolId()<0){
			throw(new Exception("You have to define a protocol first before constructing the MessageBoard ! "));
		}
		
		protocolID = factoryProperties.protocolId ;
		participantsRole = factoryProperties.participantsRole.toLowerCase();
		
		
		
		if (participantsRole.contentEquals("board")){ // exclude iboard here, since iboard is an embedded SOURCE client
			secondaryProtocolID = factoryProperties.getSecondaryProtocolId();

			globalReceipts = new GlobalReceipts(msgBoardShare);
			msgBoardShare.setGlobalReceipts(globalReceipts) ;
			
			msgBoardStorage = new MsgBoardDataStorage( msgBoardShare, factoryProperties.getConfigPath(), out ) ;
			msgBoardStorage.loadMsgBoardData() ;
			participantsStorage = msgBoardStorage;
			
			boardAddresses = new BoardAddresses();
			msgBoardShare.setBoardAddresses(boardAddresses);
			
		}else{
			factoryProperties.setSecondaryProtocolId(-1) ; 
			secondaryProtocolID = -1 ;
		}
		
		metaRegistration = new MetaRegistration(globalReceipts, msgBoardStorage);
		msgBoardShare.setMetaRegistration(metaRegistration) ;
		
		msgBoardMonitor = new MessageBoardMonitor( mbf, out) ;
		
		// will eventually create 2 instances !!
		createMessageBoardFromFactoryProperties( factoryProperties );
		 
			
		// this allows to add libraries easily
		
		ipAddressList = NicAddresses.get();
		ipAddressListStr =  arrutil.arr2text( arrutil.changeArrayStyle(ipAddressList , ""),";" );
				
		comparisonFactory = new ComparisonFactory (this) ;
		compareFactory = new CompareFactory();
		
		postConstructiveActions();
		
		createDefaultSimilarityKernel();
		msgBoardShare.setCompareKernel( compareInstance ) ; // making it global
	}

	
	private void postConstructiveActions(){
		/*
		 * different MessageBoards are always on different machines, i.e. we always use
		 * http-restlet for communication
		 * 
		 * during the startup phase, we have checked whether those remote boards 
		 * are available (?? and if not, we removed it from the list ??)
		 */
		
		if (participantsRole.contentEquals("board")){ // exclude iboard, iboard == SOURCE, so we should not create a further BoardConnector of type iboard !
			MessageBoard mebo=null ;
			if (mb.getProtocolID()==1){
				mebo=mb;
			}else{
				if ((mb2!=null) && (mb2.getProtocolID() == 1)) {
					mebo = mb;
				}
			}
			if (mebo!=null){
				msgBoardConnector = new MessageBoardConnector( this, mebo,out );
			}
			
			
			
			setupForBoardConnectionsCheck();
			
			// if addresses to other MB's are defined, then the MessageBoard will periodically check them
			// the service is contained here 
			// the checking is then provided by an inner class in "MessageBoardFactory{}": BoardLinkConnector{}

			// the threaded process then calls BoardLinkConnector.checkForBoards() below (method in inner class)
			startBoardConnectCheckService();
		}
		
		
		if (mb2!=null){
			
			mb2.setTransactions( this.transactions);
			mb.alignHandlersFor(mb2);
		}
		// comBoxFactory.setTrafficMonitor( (MessageBoardTrafficMonitorIntf)msgBoardMonitor);
	}
	
	private void createMessageBoardFromFactoryProperties( MessageBoardFactoryProperties factoryproperties ){
		
		try{

			factoryProperties.transportsInformer = this;
			
			factoryMode = factoryProperties.getFactoryMode() ;// __MESSAGEBOARD , __SEPARATED_CLIENT ;  
			if ((factoryMode<=0) || (factoryMode>=4)){
				factoryMode = MessageBoardFactoryProperties.__MESSAGEBOARD ;
			}

			
			initFields() ;
			comBoxFactory = new ComBoxFactory( factoryProperties );
			
			mbf = this;
			
			
			
			if ( factoryMode == MessageBoardFactoryProperties.__MESSAGEBOARD){
				createFactorizedBoard() ;
			}else{
				// introduceFilterPackage( factoryProperties.filter );
				prepareParticipantsStorage();
				// _prepareParticipantsDelivery("");
			}

			if (transactions==null){
				transactions = new Transactions(this);
			}
			
		
			
			openTransactions = new OpenTransactions(this) ;
			openDataDeliveries = new OpenDataDeliveries( this ) ;  
			
			System.gc();
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	
	
	private void prepareParticipantsStorage(){
		// MessageBoard is null for participants !!
		participantsStorage = new MsgBoardDataStorage( this, out);
	}
	
	private void createFactorizedBoard(){
		
		initializeFactory() ;
	}
	
	public void setProperties( MessageBoardFactoryProperties factoryproperties, boolean initcreate ){
		
		factoryProperties = factoryproperties;
		initFields();
		
		
		if (initcreate ){
			
			// transactions = new Transactions(this);
			initializeFactory() ;
		}
	}
	
	private void initFields(){
		
		configPath = factoryProperties.getConfigPath() ;
		
		remoteInstances = factoryProperties.getRemoteInstancesFlag();
		
		if (factoryProperties.getServicePort()>= 80){
			servicePort = factoryProperties.getServicePort() ;
		}
	}
		
	private void initializeFactory(){
		
		// TODO: according to protocolID the ComBox creates the requested variant
		//       which calls comBoxFactory.createComBox(), which calls createDRS(), 
		//       which creates the "physical" receiver
		
		// comBoxIntf = comBoxFactory.createComBox() ; 
		
		factoryProperties.usePrimarySettings = 1 ;
		comBoxFactory.setProtocolID( factoryProperties.getProtocolId() ) ;
		
		int fm1 = factoryProperties.factoryMode ;
		
		mb = new MessageBoard( configPath, msgBoardShare, comBoxFactory, factoryProperties.clientMatcher ) ;
		
		int fm2 = factoryProperties.factoryMode ;
		
		if (fm1!=fm2){
			
		}
		
		introduceBoardSettings(mb); // also loads buffered transactions
		int n = transactions.size();
		
		mb.reconstructContextDescriptions(transactions);
		
		// if the BoardInstance runs in dual-mode we have a secondary protocol, so we
		// open it
		
		int secPID = factoryProperties.getSecondaryProtocolId() ;
		
		if ((secPID>0) && (secPID!=factoryProperties.getProtocolId())){
			
			factoryProperties.usePrimarySettings = 2 ;
			
			int pid = factoryProperties.protocolId;
			factoryProperties.protocolId = factoryProperties.secondaryProtocolId ;
			
			this.comBoxFactory2 = new ComBoxFactory( factoryProperties );
			
			comBoxFactory2.setProtocolID( secPID ) ;
			 
			mb2 = new MessageBoard( configPath, msgBoardShare, comBoxFactory2, factoryProperties.clientMatcher ) ;
			
			factoryProperties.secondaryProtocolId = pid; 
		}
		
		out.print(1,"MessageBoard is running...");
	}  
	
	private void introduceBoardSettings( MessageBoard mb){
		

		if ( factoryProperties.getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
			mb.setFullDuplexConnection( 0 ) ;
		}else{
			mb.setFullDuplexConnection( 1 ) ;
		}
		
		transactions = new Transactions(this); // this implicitly loads all stored/buffered transactions
		mb.setTransactions( transactions ) ;
		
		// transfer port settings; note, that even if there is only 1 physical port, it is
		// necessary to set the other ports, too;
		// as purely logical "ports" they will be used to route the messages more efficiently
		mb.setSendModeForBoard( factoryProperties.getSendModeForBoard() ) ;
		mb.setVirtualPorts( factoryProperties.virtualPorts ) ;
		
		if ( factoryProperties.getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){ //if (factoryProperties.usePrimarySettings = 1 ;)
			mb.setMsgPortAllInOne( factoryProperties.getMsgPortAllInOne());
		}
		mb.setMsgPortforInterBoard( factoryProperties.getMsgPortforInterBoard() );
		mb.setMsgPortforReceptors( factoryProperties.getMsgPortforReceptors() );
		mb.setMsgPortforSources( factoryProperties.getMsgPortforSources() ) ;
		mb.setMsgPortforService( factoryProperties.getMsgPortforService() ) ;
		mb.setMsgPortforRegistration( factoryProperties.getMsgPortforSources() );
		
		futures = (Futures)getFutures( );
		mb.setFuturesReference( futures ) ;
		
		mb.setRegistrationTimeOut( factoryProperties.getRegistrationTimeOut() ) ;
		 
		mb.setModeForConflictingIDs( factoryProperties.getModeForConflictingIDs() );
		
	
	}

	public ComparisonFactory getComparisonFactory(){
		return comparisonFactory ;
	}
	public CompareFactory getCompareFactory() {
		// compareFactory is always instantiated
		return compareFactory;
	}

	/**
	 * register method for custom similarity kernels
	 * @param compare
	 * @throws Exception
	 */
	public void registerSimilarityKernel( CompareIntf compare ) throws Exception{
		
		if (compare!=null){
			compareInstance = compare ;
			comparisonFactory.setCompareKernelContainer(compareInstance);
			
			msgBoardShare.setCompareKernel( compareInstance ) ; // globalize it
			
		}else{
			throw(new Exception("provided instance for compare operations was null, nothing has been registered.")) ;
		}
	
	}
	
	public CompareIntf getCompareKernel( int createNewInstance ){
		
		if (createNewInstance>0){
			compareInstance = null;
			createDefaultSimilarityKernel();
		}
		return compareInstance;
	}
	/**
	 * initializes </br> 
	 * - comparisonFactory = factory for the organizer of comparison </br>
	 * - compareFactory    = factory for the default similarity kernel </br>
	 * - compareInstance   = the default similarity kernel </br> </br>
	 * 
	 * 
	 *  
	 */
	private void createDefaultSimilarityKernel(){
		CompareFactory cf;
		if ((compareInstance==null) ){

			if (compareFactory==null){
				comparisonFactory = new ComparisonFactory (this) ;
				compareFactory = new CompareFactory();
			}
			cf = compareFactory ;
			CompareProperties cp = new CompareProperties();
			
			cp.setDistanceMethod(2) ;
			cp.setMissingValueHandling( DistanceControlProperties.__MV_ByAdjSurrogateDistance ) ;
			
			compareInstance = cf.getCompareKernelContainer(cp) ;
			msgBoardShare.setCompareKernel( compareInstance ) ; // globalize it
			comparisonFactory.setCompareKernelContainer(compareInstance);
			
			// now the API programmer can create an instance of "DistanceControlProperties{}" if required.
		}else{
			
		}
		
	}

	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

	public void detachSource( XRegistration xRegistration ){
		Registration registration;
		
		registration = new Registration( mbf );
		
		registration.unregister(xRegistration) ;
	}

	public void detachSource( XRegistration xreg, InstanceFlavor flavor ){
		Registration registration;
		
		registration = new Registration( mbf );
		
		registration.unregister(xreg) ;
	}
	
	
	
	public static class RegisterParamPackage{
		public int regTicket;
		public String name; 
		public long instanceID;
		public String applicantName ;
		public InstanceFlavor flavor;
		public ReceptorDigestIntf resultsReceiver;
		public XRegistration xreg ;
		public int flag ;
		public int attemptNo;
	}
	
	public RegisterParamPackage getRegisterParamPackage(){
		return new RegisterParamPackage();
	}
	/*
	public XRegistration attachSource( int regTicket,  String name, 
									   long instanceID, String applicantName ,
									   InstanceFlavor flavor, ReceptorDigestIntf resultsReceiver){
	*/
	public XRegistration attachSource( RegisterParamPackage rpp ) throws Exception{
		XRegistration xReg=null; 
	
		
		try{
			

			if (rpp.flavor.getLinkRelation()<=0){
				rpp.flavor.setLinkRelation(1);
			}
			 
			BasicMessageParams basics = new BasicMessageParams();
			
			basics.instanceID = rpp.instanceID;
			basics.instanceName = rpp.name;
			basics.roleOfSender = this.participantsRole.toUpperCase(); //  "SOURCE" , or IBOARD;
			
			basics.hostaddress = factoryProperties.getRemoteIpAddress();   // default = localhost
			basics.remotePort  = factoryProperties.getRemotePort() ; // has different effects , respective the transport protocol
			
			xReg = register( rpp.regTicket, basics, rpp.applicantName, rpp.flavor,0, rpp.attemptNo);
			
			if (xReg!=null){

													out.print(4, "connectReceptor for '"+participantsRole+"': xRegistration.init(), ID = "+xReg.getInstanceID()+", listening port: "+xReg.getClientListeningPort());
													out.print(4, "installing callback to the DataConnector of the registration...");
													xReg.init(this, rpp.resultsReceiver) ;
				// xRegistration.announceFilter(this.filterPkg); // FilterPackage 

				out.printErr(1, "\n"+xReg.getRole()+" registered with instance-ID = " + xReg.getInstanceID() );
				registration = null;

			}else{
				// out.printErr(1, "Instance failed to register...");
				registration = null;
			}
			
			
		}catch(Exception e){
			registration = null;
			out.printErr(1, e.getMessage() ) ;
			
			if (connectError<0){
				throw(new Exception( e.getMessage() ));
			}
		}
		
		return xReg ;
	}
	/*
	public XRegistration openReceptor(  int regTicket,
										String name, 
										XRegistration xreg ,
										ReceptorDigestIntf receptorPerformer, 
										InstanceFlavor flavor,
										int physReceptor) {
		
	*/	
	public XRegistration openReceptor(RegisterParamPackage rpp){
		
		MessageReceptor dataInConnector;
		XRegistration xRegistration=null; 
		
		int physReceptor;
		long iid= -1;
		String applicantsName="" ;
		ReceptorDigestIntf receptorPerformer;
		// --------------------------------------
		
		physReceptor = rpp.flag ;
		receptorPerformer = rpp.resultsReceiver ;
		
		if (rpp.flavor.getLinkRelation()<=0){
			rpp.flavor.setLinkRelation(1);
		}
	 	
		if (rpp.xreg!=null){
			iid = rpp.xreg.getInstanceID() ;
			applicantsName = rpp.xreg.getNameOfApplicant() ;
		}
		 
		while ((registrationsActive>0) ){
			wait(100);
		}
		
		if (physReceptor>0){
		// we use the  "MessageReceptor" class for this, start it before any sending could happen
			
			BasicMessageParams basics = new BasicMessageParams();
			
			basics.instanceID = iid;
			basics.instanceName = rpp.name;
			basics.roleOfSender = "RECEPTOR" ;
			
			// step 1 : registering , using 
			try {
				xRegistration = register( rpp.regTicket, basics, applicantsName, rpp.flavor,0 ,rpp.attemptNo);
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (xRegistration!=null){
				// step 2 : installing the callback link to the physical task performer
				
				dataInConnector = new MessageReceptor( this, xRegistration) ;
			
				if (dataInConnector.receiver==null){
					return null;
				}
				
				// the MessageBoardFactory does not store anything about the endpoints 
				// (except the collection of "registeredInstances"), it just routes it to the registration object;
				// the "receptorPerformer" represents the object AT the endpoint, which is actually doing the
				// calculation (as an inner class callback)
				dataInConnector.setTaskPerformer(receptorPerformer) ;
				dataInConnector.setNameOfInstance( xRegistration.getNameOfApplicant() ) ;
				// correct & complete connection data for receiver are in comBoxfactoy 
				
				// this not here: taskPerformer = receptorPerformer;
				// instead, we put it to an instance of "MessageReceptor"
				receptorObserver = (Observer)dataInConnector ;
				
			}
		}else{
			
			xRegistration = receptorPerformer.getSourceRegistration();
			// receptorPerformer
			try {
				//the receiver now is connected to an Observer-update in DataConnector
				xRegistration.getDataConnector().receiver = comBoxFactory.createReceiver( xRegistration.getClientListeningPort(), 
																						  7111, // ctrlport, 
																						  xRegistration.getDataConnector(), rpp.name+"-embedded receptor");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	
	
		return xRegistration;
	}

 
	public void setupForBoardConnectionsCheck(){
		// use a separate process to check for other MB's,
		// this check is running periodically (eg 15 sec)
		// those checks are also the vehicle for exchanging admin data,
		//   spreading public host names states subscriptions etc. 
		
		// a very similar mechanism is in charge for participants
		
		boardConnectorProcedures = new BoardLinkConnector();
		 
		boardConnectService = new ReceptorsUpLinkService( this, out ) ;// factoryProperties
		boardConnectService.setPeriodLength(11000) ;
		 
	}
	
	
	/**
	 * installing the callback-object that hooks into the periodically returning thread
	 */
	private void startBoardConnectCheckService(){
		boolean relayingIsAllowed;
		// if checking for cascaded boards should be started && if available
		relayingIsAllowed = factoryProperties.getRelayingIsAllowed();
		
		if ( ( relayingIsAllowed ) && (boardConnectService != null)){
		  
			// transferring the address of our procedures by its interface "UpLinkConnectorIntf"
			boardConnectService.setBoardLinkerProcAddress( boardConnectorProcedures  ); 
			this.boardConnectService.start(); // the upLinkService knows about its factory
			 
		}
		
	}
	 
	
	
	public FilterPackageIntf introduceFilterPackage( FilterPackageIntf customFilter ){
		
		if (customFilter==null){
			filterPackage = new FilterPackage();
		}else{
			filterPackage = customFilter ;
		}
		
		filterPkg = new FilterPackage( filterPackage );
		
		return filterPackage;
	}
	
	
	/**
	 * 
	 * kind of a garbage collection for ports: we check whether the ports in the
	 * collection "reservedPorts" are really reserved, or whether they are free 
	 */
	protected void recollectingPorts(){
	
		
	}
	
	protected void addReservedPort(int port){
		Vector<Integer> rports ;
		rports = getReservedPorts();
		
		if (rports.contains(port)==false){
			getReservedPorts().add(port); 
		}
	}
	
	
	public void setProtocol( int protocolid ){
		this.protocolID = protocolid;
		
	}
	
	public int getMsgPortforRegistration(){
		int port = -1;
		
		if (mb!=null){
			port = mb.getMsgPortforRegistration();
		}
		
		if (port<0){
			port = this.comBoxFactory.getRegistrationPort() ;
		}
		return port ;
	}
	
	/**
	 * 
	 * by default, this is 7100
	 * 
	 * 
	 * @param portnumber
	 * @return
	 */
	public int setMessageBoardPortForSources( int portnumber ){
		int port = -1;
		
		// check whether it is free
		port = portnumber;
		
		if (freePort.isPortFree( port )==false){
			
			port = mb.getMsgPortforSources() ;
			
		} else{
			
			mb.setMsgPortforSources( port ) ;
		}
		
		return port ;
	}
	
	public void removeTransactionFromList( long transactionID){
		
		try{
			
			if ((openTransactions!=null) && (openTransactions.containsKey(transactionID))){
				openTransactions.removeItem(transactionID);
			}
			
		}catch(Exception e){
		}
		
	}

	
	public void stopMessageBoard( int typeOfStructure){
		
		
		if (typeOfStructure == MessageBoardFactoryProperties.__SEPARATED_CLIENT){
			  
		}
		if (typeOfStructure == MessageBoardFactoryProperties.__MESSAGEBOARD){
			stopMessageBoard();
		}
	 		
	}
	public void stopMessageBoard(){
		
		if (mbf.mb != null){
			
			messageBoardisStopping=true;
			// denying any new connection, sending messages to registered participants,
			// suggesting unregistering, making their calls persistent until the next start
			// of the MessageBoard
			out.printErr(1,"\nThe MessageBoard will be stopped!");
			
			if ((mbf!=null) && (mbf.msgBoardConnector!=null)){
				mbf.msgBoardConnector.closeEmbeddedSource();
			}
			
			out.delay(30);
			
			mbf.mb.stopMessageBoard( this.factoryProperties.getProposeClientsShutdown() ) ;
			
			
			
			out.printErr(1,"The MessageBoard has been stopped!");
		}
		
	}
	
	// ------------------------------------------------------------------------
	
	// not tested ...
	private void unregister( int enumed ){
		
		 
		Registration registration;
		XRegistration xRegistration ;
		  
		xRegistration = registeredInstances.get(enumed-1);
		                 
		registration = new Registration( mbf );
		registration.unregister( xRegistration ) ;
	
		
	}
	
	@SuppressWarnings("static-access")
	private void wait( int millis ){
	
		try {
			Thread.currentThread().sleep(millis);
		} catch (InterruptedException e) {
		}
	}
	

	public void eraseActiveRegistrationObjects(){
		
		try{
			
			registration.prepareErasureOfObject();
			registration=null;
			
		}catch(Exception e){
			// educated silenceS
		}
	}
	
	private XRegistration register( int providedTicket,
								    BasicMessageParams basics,
									// String name, String role,  //  e.g.:  "SOURCE", "RECEPTOR" 
									// long instanceID, 
								    String applicantName, // empty for new regs, filled for hibernated ones...
									InstanceFlavor flavor,
									int flag,
									int attemptNo) throws Exception{
							
		
		String myGuid ,fullname;
		int enumed = 0 ;
		
		int receptorConnexID = -1;
		
		
		XRegistration xRegistration = null ;
		
		
				// 										
		int r=0;

		// wait for registration -> we need an inner class to be threadsafe...
		// ?? we use a FiFo list for fast acceptance, then working on the items
	  
		try{
			
			// we wait outside!
			registrationsActive++;

			ticketGotActive( providedTicket );
			
			enumed = registeredInstances.size() + 1;

			if (applicantName.length()==0){
				myGuid = GUID.randomvalue();
				fullname = basics.instanceName + "-" + basics.roleOfSender.toLowerCase() + "-" + enumed + "-" + myGuid; // name should contain a GUID
			}else{
				fullname = applicantName;
			}
			
			if (registration!=null){
				eraseActiveRegistrationObjects();
			}
			connectError = 0;
			
			registration = new Registration( fullname, basics.roleOfSender, mbf, flavor, attemptNo);

			 
			// if (role.contains("RECEPT")) {
			// registration.setContentType( flavor.contentType ); // e.g. "rawdata,table");
			 
 
			r = registration.register( basics.instanceID , attemptNo); // this sends a registration message to the MB and waits for the receipt  

			// r==0 means that the process completed on the side of the registrant
			// yet, it still could have failed ( -> instanceID<=0 )
			if (r==0){
				basics.instanceID = registration.getIdFromMessageBoard();

				if (basics.instanceID > 0) {
					xRegistration = registration.getRegistrationObject();
					xRegistration.setInstanceID(basics.instanceID);
					receptorConnexID = registeredInstances.size();
					xRegistration.setReceptorConnexID(receptorConnexID);
					registeredInstances.add(xRegistration); // or registration ?
				}
										out.printErr(4, "The entity '" + basics.instanceName + "' registered by ID = "+ basics.instanceID + "  ");
			}else{
				registration=null;
				out.printErr(3, "Registration for the entity id="+basics.instanceID+" failed (code:"+r+").");
			}
			
		}catch(Exception e){
			registration=null;
			out.printErr(3,"Registration failed for \""+basics.instanceName+"\" ("+e.getMessage()+")");
		}finally{
			registrationsActive--;
		}
		
		if ((r<=-7) &&( factoryProperties.getProtocolId()<= MessageBoardFactoryProperties.protocolFTP )){
			String str="Critical error, code:"+r+"." ;
			if (r==-7){ // localhost
				str= "registering is not possible, Messageboard seems to be down.";
			}
			if (r==-8){ // remote
				str= "registering is not possible, remote host or remote Messageboard seems to be down.";
			}
			this.connectError=r; 
			out.printErr(4,"MessageBoardFactory::register() : "+str);
			// TODO: do NOT throw an error if we are registering...
			throw(new Exception(str)); 
		}else{
			
		}
		return xRegistration;
		
	}
	
	public void waitForTicket(int ticket, int maxSeconds) {
		int p,z=0;
		
		while (mbf.callNextTicket() != ticket) {
			wait(100);
			z++;
			if (z*10>maxSeconds){
				p = issuedTickets.indexOf(ticket) ;
				if (p>=0){
					issuedTickets.remove(p) ;
				}
				break;
			}
		}
	}

	
	public void waitForRegstrations(int count){
		
		while ( this.registeredInstances.size() < count){
			wait(100);
		}
		
	}
	
	public void setFilterActivation( boolean activationState ) {
		msgfilterIsActivated = activationState ;
	}

	public boolean getFilterActivation(){
		
		return msgfilterIsActivated ;
	}

	public MessageBoard getMessageBoard(){
		return mb;
	}

	public MsgBoardDataStorage getMessageBoardStorage(){
		// MessageBoard is null for participants !!
		if (mb!=null){
			return mb.getMsgBoardStorage();
		} else{
			if (this.participantsRole.toLowerCase().contains("board")){
				return participantsStorage;
			}else{
				return participantsStorage;
			}
		}
			
	}

	public MessageBoardShare getMsgBoardShare() {
		return msgBoardShare;
	}

	public MetaRegistration getMetaRegistration() {
		return metaRegistration;
	}

	public MessageBoardConnector getMsgBoardConnector() {
		return msgBoardConnector;
	}

	public MessageBoardTrafficMonitorIntf getMsgBoardMonitor() { // MessageBoardTrafficMonitorIntf 
		  return msgBoardMonitor;
	}

	public Observer getReceptorObserver() {
		return receptorObserver;
	}

	public void saveOpenTransactions(){
		openTransactions.save();
	}
	
	public void restoreOpenTransactions( String name, long spec){
		// called from participant upon startup
		// needs a name and a serial ...
		
		openTransactions.createFilename(  name,   0);
		openTransactions.load() ;
	}
	
	public SortedMap<Long, String> getOpenTransactions() {
		if (openTransactions==null){
			openTransactions = new OpenTransactions(this) ;
		}
		return openTransactions.getTransactions();
	}

	public OpenDataDeliveries getOpenDataDeliveries() {
		return openDataDeliveries;
	}

	public Transactions getTransactions() {
		return transactions;
	}

	
	public Vector<XRegistration> getRegisteredInstances() {
		return registeredInstances;
	}

	public ComBoxFactory getComBoxFactory() {
		return comBoxFactory;
	}

	public MessageBoardFactoryProperties getFactoryProperties(){
		return  factoryProperties;
	}

	public ParticipantsDeliveryPadIntf getDeliveryPad(String role, String specsID){
		
		deliveryPad=null ;
		
		
		if (deliveryPad==null){
			deliveryPad = new ParticipantsDeliveryPad(this,role,specsID, out) ;
			// XRegistration xreg = mbf.getRegisteredInstances().get(0) ;
			// deliveryPad.setRegistrationObject(xreg);
		}
		
		return deliveryPad ;
	}
	
	
	public TouchBoxIntf getTouchBox(){
		if (touchBox==null){
			touchBox = new TouchBox(this,out) ;
		}
		
		return touchBox;
	}

	/**
	 * 
	 * @return
	 */
	public FuturesIntf getFutures( ){
		
		if (futures==null){
			 
			futures = new Futures(out);
			futures.setRoleOfParticipant( factoryProperties.participantsRole ) ;
			
			if (mb!=null){
				// this is the version for the participant
				mb.setFuturesReference(futures) ;
			}

		}
		return futures;
	}

	
	
	/**
	 *  this is the version for the participants
	 *   
	 * @param xreg
	 * @return
	 */
	public FuturesIntf getFutures( XRegistration xreg ){
		if (futures==null){
			 
			futures = new Futures( xreg, out);
			futures.setRoleOfParticipant( factoryProperties.participantsRole ) ;
			
			// mb.setFuturesReference(futures) ;
			// org.NooLab.glue.subscription.Futures@d175ff 
		}
		return futures;
	}

	public void setMessageReceptorReference( MessageReceptor msgrec){
		messageReceptor = msgrec;
	}
	
	public MessageReceptor getMessageReceptorReference() {
		return messageReceptor;
	}

	public void setParticipantsReference( MessageBoardParticipant mbparticip ){
		participant = mbparticip;
	}
	
	public MessageBoardParticipant getParticipantReference() {
		return participant;
	}

	/**
	 * the privateTransactionIdentifier, which is stored together with the transaction ID is
	 * needed to prevent sniffing (diverting) transactions, simply by probing combinations of
	 * T-IDs, instance-IDs 
	 * (note, that the MessageBoard itself is allowed to inspect the files (especially the expired ones)) 
	 * 
	 * 
	 * @param transactionID
	 * @return
	 */
	public String getSecretOfTransaction( long transactionID){
		
		String privateTransactionIdentifier ="";
		try{
			
			if ((openTransactions!=null) && (openTransactions.containsKey(transactionID))){
				privateTransactionIdentifier = openTransactions.getStringID(transactionID);
			}
			
		}catch(Exception e){
		}
		
		return privateTransactionIdentifier ;
	}

	public String getConfigPath() {
		return configPath;
	}

	public void setConfigPath(String configPath) {
		this.configPath = configPath;
	}

	public int getServicePort(){
		
		return servicePort ;
	}

	public Vector<Integer> getReservedPorts() {
		return reservedPorts;
	}

	public ArrayList<String> getIpAddressList() {
		return ipAddressList;
	}

	public String getIpAddressListStr() {
		return ipAddressListStr;
	}

	public int getRegistrationsActive() {
		return registrationsActive;
	}

	public void setRegistrationsActive(int registrationsActive) {
		this.registrationsActive = registrationsActive;
	}
	
	public boolean isDirectConnectionsAmongParticipants() {
		return directConnectionsAmongParticipants;
	}

	public boolean getDirectConnectionsAmongParticipants() {
		return directConnectionsAmongParticipants;
	}

	public void setDirectConnectionsAmongParticipants(
			boolean directConnectionsAmongParticipants) {
		this.directConnectionsAmongParticipants = directConnectionsAmongParticipants;
	}

	public int getTicket(){
		int tv;
		
		tv = latestTicketValue;
		tv++;
		issuedTickets.add(tv);
		
		latestTicketValue++;
		
		return latestTicketValue ;
	}
	
	/**
	 * we will only return the next ticket, if (registrationsActive==0) => the register procedure has been left
	 * @return
	 */
	public int callNextTicket(){
		int itv = -1;
		if (( registrationsActive==0) && (issuedTickets.size()>0)){
			wait(10);
			itv = issuedTickets.get(0);
		}
		return itv ;
	}

	public void ticketGotActive( int itv ){
		int p;
		
		if (issuedTickets.size()>0){
			p =  issuedTickets.indexOf(itv);
			if (p>=0){
				issuedTickets.remove(p);
			}
		}
	}

	/**
	 * 
	 * that is a callback from the receiver in the TcpBox, which is signaling its own stopping,
	 * x
	 */
	@Override
	public int setServerState(int port, int state) {
		 
		out.print(3, ">>>> update of state for receiver listening on port "+port+": "+state) ;
		
		if (messageBoardisStopping==true){
			return -1;
		}
		
		try{
			if (mb!=null){
			if ((port == mb.getMsgPortforRegistration()) && (state < 0)) {
				out.print(1, ">>>> restarting Message Board Server for port "+port ); 
				mb.createBoardReceiver("registry");
			}

			if ((port == mb.getMsgPortforSources()) && (state < 0)) {
				out.print(1, ">>>> restarting Message Board Server for port "+port );
				mb.createBoardReceiver( participantsRole.toLowerCase()) ; // "source");
				
			}

			if ((port == mb.getMsgPortforReceptors()) && (state < 0)) {
				out.print(1, ">>>> restarting Message Board Server for port "+port );
				mb.createBoardReceiver("receptor");
			}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return 0;
	}
	
	 
	class BoardLinkConnector implements BoardConnectorIntf{

		@Override
		public void checkAvailability() {
			
			
		}
 
		@Override
		public void setBusyFlag(boolean value) {
			
		}

		@Override
		public boolean getBusyFlag() {
			
			return false;
		}

		@Override
		public int getExitValue() {
			
			return 0;
		}
		
		@Override
		public void checkForBoards(int indicator) {
			 								out.print(5, "messageboardfactory checking for cascading messageboards...");
			 								
			int nm;
			boolean cb=false, previousAvail;
			String pageRequest, str, host_to_check;
			Vector<RemoteAddress>  addresses ;
			RemoteAddress address;
			BoardAddress boardAddress ;
			ComBox  combox;
			
			
			
			addresses = mbf.factoryProperties.getRemoteAddresses();
			
			if (indicator<=0){
				out.print(3, "process for checking availability of remote MessageBoards has been started...");
			}else{
				out.print(4, "checking availability of remote MessageBoards : "+addresses.size()+" addresses in the list ...");
			}
			pageRequest = "/vp7100/" ; 	// the virtual port for source requests, 
										// should be always present if MB is running at all
			
			nm = addresses.size() ;
			combox = comBoxFactory.getComBox();
			
			for (int i=0;i<nm;i++){
				address = addresses.get(i) ;
				host_to_check = address.getHostname() ;
				address.getPort() ;
				
				if (host_to_check.contains("127.0.0.")){
					continue;
				}
				boardAddress = boardAddresses.set(address);
				// for a connection check we do not use restlet, just a standard http request
				if (boardAddress!=null){
					
					if (boardAddress.getState()==BoardAddress.__BA_DEACTIVED){
						continue;
					}
					
					previousAvail = boardAddress.isAvailable();
					cb = combox.checkHttpConnectivity(boardAddress, pageRequest);
					// cb=false: general failure, other results are inside boardAddress!
					if (cb==false){
						
					}
						
													str = "remote address ("+boardAddress.getAddress().getHostname()+") "+
														  "checked: avail="+boardAddress.isAvailable()+"  ,  active="+boardAddress.isActive() ;
													if (boardAddress.isAvailable() && boardAddress.isActive()){
														out.print(4, str);
													} else{
														if (previousAvail){
															out.printErr(2, str+", remote MessageBoard not available and more! ");
															msgBoardConnector.removeBoardAddress(boardAddress);
														}else{
															out.printErr(4, str);
														}
													}
													
	                if (boardAddress.isAvailable() && boardAddress.isActive()){
	                	// we just send a ping with our address to the remote board,
	                	// this will return then a confirmation, comprising it's IP, a secret, 
	                	// and the current number of registered receptors
	                	// if that number is =0 the confirmation will set to false !!
	                	
	                	if (factoryProperties.getRelayingIsAllowed()){
	                		
	                		msgBoardConnector.sendIBoardPing(boardAddress) ;
	                	
	                	}
	                	
	                }
				} // boardAddress!=null ?

			} // i->
			
		}
		
	}
} // main:: MessageBoardFactory
 

class RegistrationSets{
	
	Vector<RegistrationSet> regSets = new Vector<RegistrationSet>();
	
	public RegistrationSets(){
		
	}
	
	public void createSet( String name, String role, InstanceFlavor flavor ){
		RegistrationSet regset ;
		
		regset = new RegistrationSet(name, role, flavor); 
		regSets.add(regset) ;
	}
	
}

class RegistrationSet{
	
	String Name;
	String Role;
	InstanceFlavor Flavor;
	
	public RegistrationSet( String name, String role, InstanceFlavor flavor){
		 Name = name ; 
		 Role = role;
		 Flavor = flavor ;
	}
}
